﻿using System.Collections.Generic;
using UnityEngine;

/// <summary>
/// Provides extension suport for <see cref="Transform"/>.
/// </summary>
public static class TransformExtension
{
    public static IEnumerable<Transform> FindChildrenByTag(this Transform transform, string tag)
    {
        foreach (Transform child in transform)
        {
            if (!child.CompareTag(tag)) continue;
            yield return child;
        }
    }
    public static void FindChildrenByTag(this Transform transform, string tag, IList<Transform> result)
    {
        foreach (Transform child in transform)
        {
            if (child.CompareTag(tag)) result.Add(child);
            FindChildrenByTag(child, tag, result);
        }
    }

    public static IEnumerable<T> FindChildrenByTag<T>(this Transform transform, string tag) where T : Component
    {
        foreach (Transform child in transform)
        {
            if (!child.CompareTag(tag)) continue;
            yield return child.GetComponent<T>();
        }
    }
    public static void FindChildrenByTag<T>(this Transform transform, string tag, IList<T> result) where T : Component
    {
        foreach (Transform child in transform)
        {
            if (child.CompareTag(tag)) result.Add(child.GetComponent<T>());
            FindChildrenByTag(child, tag, result);
        }
    }

    public static Transform? FindChildByTag(this Transform self, string tag, bool recursive = false)
    {
        if (recursive)
        {
            foreach (Transform child in self)
            {
                if (child.CompareTag(tag)) return child;
                Transform result = FindChildByTag(child, tag);
                if (result) return result;
            }
        }
        else
        {
            foreach (Transform child in self)
            {
                if (!child.CompareTag(tag)) continue;
                return child;
            }
        }
        return null;
    }
    public static T? FindChildByTag<T>(this Transform self, string tag, bool recursive = false) where T : Component
    {
        if (recursive)
        {
            foreach (Transform child in self)
            {
                if (child.CompareTag(tag)) return child.GetComponent<T>();
                T result = FindChildByTag<T>(child, tag);
                if (result) return result;
            }
        }
        else
        {
            foreach (Transform child in self)
            {
                if (!child.CompareTag(tag)) continue;
                return child.GetComponent<T>();
            }
        }
        return null;
    }

    public static Transform? FindChildByName(this Transform transform, string name, bool recursive = false)
    {
        if (recursive)
        {
            foreach (Transform child in transform)
            {
                if (child.name == name) return child;
                Transform result = FindChildByName(child, name);
                if (result) return result;
            }
        }
        else
        {
            foreach (Transform child in transform)
            {
                if (child.name != name) continue;
                return child;
            }
        }
        return null;
    }

    public static void DestroyAllChildren(this Transform self)
    {
        if (Application.isPlaying) foreach (Transform child in self) Object.Destroy(child.gameObject);
        else while (self.childCount > 0) Object.DestroyImmediate(self.GetChild(0).gameObject);
    }

    public static void GetChildren(this Transform self, IList<Transform> result, bool recursive = false)
    {
        if (recursive)
        {
            result.Clear();
            RecursiveGet(self, result);
        }
        else foreach (Transform child in self) result.Add(child);

        static void RecursiveGet(Transform transform, IList<Transform> result)
        {
            foreach (Transform child in transform)
            {
                result.Add(child);
                RecursiveGet(child, result);
            }
        }
    }
    public static IEnumerable<Transform> GetChildren(this Transform self, bool recursive = false)
    {
        if (recursive) foreach (var child in RecursiveGet(self)) yield return child;
        else foreach (Transform child in self) yield return child;

        static IEnumerable<Transform> RecursiveGet(Transform transform)
        {
            foreach (Transform child in transform)
            {
                yield return child;
                foreach (var grandchild in RecursiveGet(child)) yield return grandchild;
            }
        }
    }
    public static void GetChildrenPath(this Transform self, List<string> result, bool recursive = false)
    {
        if (recursive)
        {
            result.Clear();
            RecursiveGet(self, string.Empty, result);
        }
        else foreach (Transform child in self) result.Add(child.name);

        static void RecursiveGet(Transform transform, string path, List<string> result)
        {
            foreach (Transform child in transform)
            {
                string currentPath = $"{path}{child.name}";
                result.Add(currentPath);
                RecursiveGet(child, $"{currentPath}/", result);
            }
        }
    }
    public static IEnumerable<string> GetChildrenPath(this Transform self, bool recursive = false)
    {
        if (recursive) foreach (var path in RecursiveGet(self, string.Empty)) yield return path;
        else foreach (Transform child in self) yield return child.name;

        static IEnumerable<string> RecursiveGet(Transform transform, string path)
        {
            foreach (Transform child in transform)
            {
                string currentPath = $"{path}{child.name}";
                yield return currentPath;
                foreach (var childPath in RecursiveGet(child, $"{currentPath}/")) yield return childPath;
            }
        }
    }
}